package com.zhoulz.homework.chaptor02.node;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.internal.StringUtil;

import java.util.List;

import org.apache.curator.framework.CuratorFramework;
import org.apache.log4j.Logger;

import com.zhoulz.homework.chaptor02.utils.Util;
import com.zhoulz.homework.chaptor02.zk.Zk;

/**
 * 服务端
 * @author zhoulz
 *
 */
public class Server {
	Logger log = Logger.getLogger(Server.class);
	
	private String name; 
	private String host; 
	private int port;
	private boolean keepAlive; 
	
	private ChannelFuture cf;
	private PacketServerChannelInboundHandler inboundHandler;
	private PacketServerChannelOutboundHandler outboundHandler;
	private EventLoopGroup bossGroup = new NioEventLoopGroup(1);
	private EventLoopGroup workerGroup = new NioEventLoopGroup();
	/**
	 * constructor
	 * @param name 服务节点名
	 * @param host 服务端ip,默认 127.0.0.1
	 * @param port 服务端端口
	 * @param keepAlive
	 */
	public Server(String name, String host, int port, boolean keepAlive){
		this.name = name;
		this.host = host;
		this.port = port;
		this.keepAlive = keepAlive;
	}
	
	public void start(CuratorFramework client) throws Exception{
		log.info("节点服务器正在启动...");
		try {
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_KEEPALIVE, keepAlive)
			.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					inboundHandler = new PacketServerChannelInboundHandler();
					outboundHandler = new PacketServerChannelOutboundHandler();
					// 解码/出站是逆序执行，编码/入站是顺序执行
					// 执行顺序：LengthFieldBasedFrameDecoder -> PacketDecode -> inboundHandler -> PacketEncode -> outboundHandler
					ch.pipeline().addLast(
						outboundHandler,
						new PacketEncode(),
						new LengthFieldBasedFrameDecoder(65538, 0, 2, 1, 0){

							@Override
							protected Object decode(ChannelHandlerContext ctx,
									ByteBuf in) throws Exception {
//								System.out.println("length decode: "+ in.readableBytes());
								return super.decode(ctx, in);
							}
							
						},
						new PacketDecode(),
						inboundHandler
						);
				}
			});
			cf = bootstrap.bind(host, port).sync();
			log.info(String.format("节点%s服务器启动成功，并监听端口：%d", name, port));
			// 添加到操作类，供command用
			CmdHelper.setServer(this);
			if(client.getZookeeperClient().isConnected()){
				// 注册节点
				Zk.registerNode(this, this.name);
				// 监视节点
				Zk.initCache(client, this);
				
			}else{
				log.info("未连接到zk server，此节点未启动任何zk监听。");
			}
			
			cf.channel().closeFuture().sync();
		} catch (Exception e) {
			log.error("节点服务器启动失败", e);
		}finally{
			workerGroup.shutdownGracefully().sync();
			bossGroup.shutdownGracefully().sync();
		}
	}
	public void shutdown(){
		log.info("server closing...");
		try {
			if(outboundHandler != null){
				outboundHandler.setStop(true);
				cf.await();
			}
			cf.channel().close();
		} catch (Exception e) {
			log.error("server停止失败！", e);
		}
		log.info("server closed");
	}
	public String getReceiveFiles(){
		StringBuilder sb = new StringBuilder(StringUtil.NEWLINE).append(ReceiveFile.header).append(StringUtil.NEWLINE);
		if(inboundHandler != null){
			List<ReceiveFile> files = inboundHandler.getReceiveFiles();
			if(!Util.empty(files)){
				for (ReceiveFile file : files) {
					sb.append(file).append(StringUtil.NEWLINE);
				}
			}else{
				sb.append("no files").append(StringUtil.NEWLINE);
			}
		}else{
			sb.append("no files").append(StringUtil.NEWLINE);
		}
		return sb.toString();
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public boolean isKeepAlive() {
		return keepAlive;
	}

	public void setKeepAlive(boolean keepAlive) {
		this.keepAlive = keepAlive;
	}
}
